package handler

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"forum/pkg/domain"
	"forum/pkg/model/helper"
	"forum/pkg/model/session"

	"github.com/gorilla/sessions"
)

type Payload struct {
	Username string
	Password string
}

type userHandler struct {
	userSvc domain.UserService
}

func NewUserHandler(userSvc domain.UserService) domain.UserHandler {
	return &userHandler{
		userSvc: userSvc,
	}
}

func (h *userHandler) Login(w http.ResponseWriter, r *http.Request) {
	// parse request
	payload := Payload{}
	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
		d := fmt.Errorf("password or username is error : %v", err)
		helper.ResponseErrJSON(w, http.StatusBadRequest, d)
		return
	}

	// authenticate user
	user, err := h.userSvc.LoginAuthByPassword(payload.Username, payload.Password)
	if err != nil {
		d := fmt.Errorf("password or username is error: %v", err)
		helper.ResponseErrJSON(w, http.StatusUnauthorized, d)
		return
	}

	// build session
	if err := h.buildSession(w, r, user); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println(err)
		return
	}

	helper.ResponseText(w, http.StatusOK, "")
}

func (h *userHandler) Logout(w http.ResponseWriter, r *http.Request) {
	sess, err := session.GetSession(r)
	if err != nil {
		helper.ResponseText(w, http.StatusNoContent, "")
		log.Println(err)
		return
	}

	// clean session
	if err := h.cleanSession(w, r, sess); err != nil {
		log.Println(fmt.Errorf("clean session failed: %v", err))
	}

	helper.ResponseText(w, http.StatusNoContent, "")
}

func (h *userHandler) Register(w http.ResponseWriter, r *http.Request) {
	// get form information
	r.ParseForm()
	user := &domain.User{
		Name:     r.Form.Get("name"),
		Email:    r.Form.Get("email"),
		Password: r.Form.Get("password"),
	}

	// check user
	if err := h.userSvc.CheckUser(user); err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("your messages error : %v", err))
		return
	}

	// insert into mysql
	if err := h.userSvc.CreateUser(user); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		log.Println(err)
		return
	}

	helper.ResponseText(w, http.StatusCreated, "Successfally registered user")
}

func (h *userHandler) ResetPassword(w http.ResponseWriter, r *http.Request) {
	payload := &struct {
		Name        string `json:"name"`
		Email       string `json:"email"`
		NewPassword string `json:"new_password"`
		ReInput     string `json:"re_input"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(payload); err != nil {
		helper.ResponseText(w, http.StatusBadRequest, "")
		return
	}
	if payload.NewPassword != payload.ReInput {
		helper.ResponseErrJSON(w, http.StatusBadRequest, fmt.Errorf("the two passwords do not match"))
		return
	}

	// get user by email and username
	newUser := &domain.User{
		Name:     payload.Name,
		Email:    payload.Email,
		Password: payload.NewPassword,
	}
	user, err := h.userSvc.CheckUserFields(newUser)
	if err != nil {
		helper.ResponseErrJSON(w, http.StatusBadRequest, err)
		return
	}

	// update password
	if err := h.userSvc.UpdateUser(user); err != nil {
		helper.ResponseText(w, http.StatusInternalServerError, "")
		return
	}

	helper.ResponseText(w, http.StatusCreated, "")
}

func (h *userHandler) buildSession(w http.ResponseWriter, r *http.Request, user *domain.User) error {
	sess, err := session.GetSession(r)
	if err != nil {
		return err
	}

	sess.Values["user_id"] = user.UUID
	sess.Values["user_email"] = user.Email
	sess.Values["user_name"] = user.Name
	if err := sess.Save(r, w); err != nil {
		return err
	}

	return nil
}

func (h *userHandler) cleanSession(w http.ResponseWriter, r *http.Request, sess *sessions.Session) error {
	for key := range sess.Values {
		delete(sess.Values, key)
	}
	sess.Options.MaxAge = -1

	if err := sess.Save(r, w); err != nil {
		return err
	}

	return nil
}
